/***********************************************************************************************************************
 * ############################################# Application AdewebLite #############################################
 * AdewebLite Android est une application permettant de consulter des emplois du temps fournis par l'université UHP
 * avec ou sans connexion internet (informations gardées en mémoire à chaque synchronisation). Et utilisant la web
 * API ADE pour collecter les données.
 * @author Andrianjafintrimo Victor, Zanette Sébastien
 * 
 * Basée sur l'application du même nom, utilisant le flux RSS (données à traiter), de :
 * @author Schnell Michaël, Zanette Sébastien
 * **********************************************************************************************************************
 */

package ade.prime.model;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;

/**
 * Comparaison de 2 emplois du temps et interclassement par ordre croissant d'id.
 * @author Zanette Sébastien, @author Andrianjafintrimo Victor
 */
public class GuidScheduleUpdater extends ScheduleUpdater {
	
	@SuppressWarnings("unused")
	private static final String TAG = GuidScheduleUpdater.class.getSimpleName() ;

	protected ArrayList<Lesson> oldLessons ;
	
	private static final GregorianCalendar calendar = new GregorianCalendar() ;
	
	//TODO Reset to correct value
	private static final int CLOSE_INTERVAL = 2 ; 

	/**
	 * Creer une instance de <code>GuidScheduleUpdater</code>.
	 */
	public GuidScheduleUpdater() {
		super() ;
	}// GuidScheduleUpdater()

	/**
	 * Comparaison de 2 emplois du temps, basé sur l'id des lessons.
	 * @param oldS ancien emploi du temps (dans la base de données).
	 * @param newS emploi du temps récupéré
	 */
	@Override
	protected void compareS(Schedule oldS, Schedule newS) {
		this.insert.ensureCapacity(newS.getNLessons()) ;
		this.update.ensureCapacity(oldS.getNLessons()) ;
		this.delete.ensureCapacity(oldS.getNLessons()) ;
		this.buildOldGuids(oldS) ;
		for(Lesson newLesson : newS) {
			String newGuid = newLesson.getGuid() ;
			// lesson is old => guid known
			int indexOf = this.indexOf(newGuid) ;
			if (indexOf != -1) {
				// lesson is changed => update
				Lesson oldLesson = GuidScheduleUpdater.getLesson(oldS, newGuid) ;
				if (!oldLesson.equals(newLesson)) {
					this.importantChange = this.importantChange 
							|| isClose(newLesson.getDate()) ;
					newLesson.setLessonId(oldLesson.getLessonId()) ;
					
//					Log.d(TAG, String.format("Old: (time, room) = ('%s', '%s')",
//							oldLesson.getTime(), oldLesson.getRoom())) ;
//					Log.d(TAG, String.format("New: (time, room) = ('%s', '%s')",
//							newLesson.getTime(), newLesson.getRoom())) ;
					
					if (!oldLesson.getTime().equals(newLesson.getTime())
							|| !oldLesson.getRoom().equals(newLesson.getRoom())) {
						newLesson.setHistory(oldLesson) ;
					}// if
					this.update.add(newLesson) ;
					newLesson.setType(oldLesson.getType()) ;
					//change le statut de représentant le niveau de changement
					newLesson.setChange(oldLesson.getLevelChange(newLesson)) ;
				}// if
				this.removeId(indexOf) ;
			} else {
				// lesson is new => guid unknown
				this.insert.add(newLesson) ;
				this.importantChange = this.importantChange 
						|| isClose(newLesson.getDate()) ;
			}// if
		}// for
		// The remaining lessons have to be removed
		for(Lesson lesson : this.oldLessons) {
			this.importantChange = this.importantChange 
					|| isClose(lesson.getDate()) ;
			this.delete.add(lesson) ;
		}// for
		this.oldLessons = null ;
//		Log.d("GuidScheduleUpdater", String.format("(I, U, D, ic) = (%d, %d, %d, %b)",
//				Integer.valueOf(this.insert.size()),
//				Integer.valueOf(this.update.size()),
//				Integer.valueOf(this.delete.size()),
//				Boolean.valueOf(this.importantChange))) ;
	}// compareS(Schedule, Schedule)
	
	/**
	 * Indique si la date donnée se trouve dans les 2 jours qui suivent.
	 * 
	 * <p>Exemple: date = {@code "05/06/2011"}<br>
	 * Si date du jours = {@code "01/06/2011"}, 
	 * alors la fonction retourne {@code false}<br>
	 * Si date du jours = {@code "04/06/2011"}, 
	 * alors la fonction retourne {@code true}
	 * 
	 * @param date date à vérifier
	 * @return {@code true} si la date donnée se trouve dans les 2 jours qui suivent,
	 * 			{@code false} sinon
	 */
	private static boolean isClose(Date date) {
		Date now = calendar.getTime() ;
//		Log.d(TAG, String.format("[isClose] (%s), now: %s", 
//				date, now)) ;
		int nowDayYear = calendar.get(Calendar.DAY_OF_YEAR) ;
		
		calendar.setTime(date) ;
		int dayYear = calendar.get(Calendar.DAY_OF_YEAR) ;

//		Log.d(TAG, String.format("   nowDay: %d, day: %d (%d)", 
//				nowDayYear, dayYear, dayYear + 365)) ;
		if (dayYear < nowDayYear) {
			dayYear += 365 ;
		}// if
		
		boolean close = (dayYear - nowDayYear <= CLOSE_INTERVAL) ;
		calendar.setTime(now) ;
//		Log.d(TAG, String.format("   >>> after: %s", calendar.getTime())) ;
		
		return close ;
	}// isClose(Date)

	/**
	 * Construit une liste d'emploi du temps par ordre croissant d'id grâce au tri par insertion.
	 * @param schedule emploi du temps
	 */
	protected void buildOldGuids(Schedule schedule) {
		this.oldLessons = new ArrayList<Lesson>(schedule.getNLessons()) ;
		if (schedule.getNLessons() == 0) {
			return ;
		}// if
		Iterator<Lesson> iterator = schedule.iterator() ;
		this.oldLessons.add(iterator.next()) ;
		while (iterator.hasNext()) {
			Lesson lesson = iterator.next() ;
			int insertPosition = this.oldLessons.size() ;
			String lessonId = lesson.getGuid() ;
			while ((insertPosition > 0) 
					&& (this.oldLessons.get(insertPosition-1).getGuid().compareTo(lessonId) > 0)) {
				insertPosition-- ;
			}// while
			this.oldLessons.add(insertPosition, lesson) ;
		}// while
	}// buildOldIds(Schedule)

	/**
	 * Retourne l'indice de la liste du cours passé grâce à son id.
	 * Le recherche est dichotomique.
	 * @param id id du cours
	 * @return
	 */
	protected int indexOf(String id) {
		boolean found = false ;
		int left = 0, right = this.oldLessons.size()-1 ;
		int middle = 0 ;
		while ((left <= right) && !found) {
			middle = (left + right) / 2 ;
			String currentId = this.oldLessons.get(middle).getGuid() ;
			int c = currentId.compareTo(id) ;
			if (c == 0) {
				found = true ;
			} else if (c < 0) {
				left = middle+1 ;
			} else/* c > 0 */{
				right = middle-1 ;
			}// if
		}// while

		if (found) {
			return middle ;
		}// if
		return -1 ;
	}// isOldId(long)

	/**
	 * Supprime un element de la liste de cours (cours de la BD).
	 * @param removedPosition 
	 */
	protected void removeId(int removedPosition) {
		this.oldLessons.remove(removedPosition) ;
	}// removeId(long)

	/**
	 * Retourne le cours identifié par l'id en paramètre.
	 * @param schedule emploi du temps
	 * @param lessonguid id du cours
	 * @return cours identifié par l'id, null si pas de resultat
	 */
	protected static Lesson getLesson(Schedule schedule, String lessonguid) {
		for(Lesson lesson : schedule) {
			if (lesson.getGuid().equals(lessonguid) 
					/*&& (lesson.getScheduleId() == schedule.getId())*/) {
				return lesson ;
			}// if
		}// for
		return null ;
	}// getLesson(Schedule schedule, long)

}// class GuidScheduleUpdater